package proj.pt.services;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import org.springframework.util.StringUtils;

import proj.pt.model.Course;
import proj.pt.model.Department;
import proj.pt.model.Exam;
import proj.pt.model.Result;
import proj.pt.model.SubCourse;
import proj.pt.model.Subject;
import proj.pt.model.SubjectAllotment;
import proj.pt.model.User;
import proj.pt.model.dao.Dao;
import proj.pt.web.ExamResultFilter;
import proj.pt.web.PerformanceResultFilter;
import proj.pt.web.SubjectFilter;
import proj.pt.web.UserFilter;

public class LogicProcessorImpl implements LogicProcessor {

	private Dao dao;

	public Dao getDao() {
		return dao;
	}

	public void setDao(Dao dao) {
		this.dao = dao;
	}
	
	// -- Department related
	public List<Department> getDepartments(){
		return dao.getDepartments();
	}
	public Department getDepartment(Long id){
		return dao.getDepartment(id);
	}
	public void saveOrUpdateDepartment(Department department){
		if(department.getCreatedDate() == null)
			department.setCreatedDate(new Date());
		dao.saveOrUpdateDepartment(department);
	}
	public void deleteDepartment(Department department){
		dao.deleteDepartment(department);
	}
	
	/**
	 * This method is to validate the department id that it holds. return true for a valid
	 * department id, else return false.
	 * @param department
	 * @return
	 */
	public boolean validateDepartmentId(Department department){
		if(department != null && StringUtils.hasLength(department.getDeptId())){
			List<Department> list = getDepartments();
			if(list != null && list.size() > 0){
				/*Department eachDept = null;
				for(int intx = 0; intx < list.size(); intx++){
					eachDept = (Department)list.get(intx);
					if(eachDept != null  
							&& !eachDept.getId().equals(department.getId())
							&& eachDept.getDeptId().equalsIgnoreCase(department.getDeptId())){
						return false;
					} //end if
				} //end for */
			
				for(Department eachDept:list){
					if(eachDept != null  
							&& !eachDept.getId().equals(department.getId())
							&& eachDept.getDeptId().equalsIgnoreCase(department.getDeptId())){
						return false;
					} //end if
				} //end for
			
			} //end if
			
			return true;
		} //end if
		
		return false;
	}
	
	
	
	// -- Course related
	/**
	 * Get the courses list filtered by access privilege.
	 */
	public List getCourses(User currentUser){
		if(currentUser != null){
			if(Role.getAdminRoles().keySet().contains(currentUser.getRoleId())){
				// if current user has Admin role, then just retrive all courses
				return dao.getCourses();
				
			} else if(Role.getHODRoles().keySet().contains(currentUser.getRoleId())){
				// if current courses for HOD's  department
				return getCourses(currentUser.getDepartment());
			} //
		} //end if
		
		return null;
	}
	public Course getCourse(Long id){
		return dao.getCourse(id);
	}
	public List getCourses(Department department){
		return dao.getCourses(department);
	}
	public Set getCourseBatches(Course course){
		Set batches = new HashSet();
		if(course != null){
			Course courseFromDb = dao.getCourse(course.getId());
			if(courseFromDb != null){
				return courseFromDb.getBatch();
			} //end if
		} //end if
		return batches;
	}
	public Set getSubCourses(Course course){
		Set subcourses = new HashSet();
		if(course != null){
			Course courseFromDb = dao.getCourse(course.getId());
			if(courseFromDb != null){
				return courseFromDb.getSubCourse();
			} //end if
		} //end if
		return subcourses;
	}
	public List getAllCourseSemesters(){
		return dao.getSubCourses();
	}
	public SubCourse getSubCourse(Long id){
		return dao.getSubCourse(id);
	}
	public void saveOrUpdateCourse(Course course){
		if(course.getCreatedDate() == null)
			course.setCreatedDate(new Date());
		dao.saveOrUpdateCourse(course);
	}
	public void deleteCourse(Course course){
		dao.deleteCourse(course);
	}
	
	/**
	 * This method is to validate the course id that it holds. return true for a valid
	 * course id, else return false.
	 * @param department
	 * @return
	 */
	public boolean validateCourseId(Course course){
		if(course != null && StringUtils.hasLength(course.getCourseId())){
			List<Course> list = dao.getCourses();
			if(list != null && list.size() > 0){
				/*Course eachCourse = null;
				for(int intx = 0; intx < list.size(); intx++){
					eachCourse = (Course)list.get(intx);
					if(eachCourse != null  
							&& !eachCourse.getId().equals(eachCourse.getId())
							&& eachCourse.getCourseId().equalsIgnoreCase(course.getCourseId())){
						return false;
					} //end if
				} //end for */
				
				for(Course eachCourse:list){
					if(eachCourse != null  
							&& !eachCourse.getId().equals(eachCourse.getId())
							&& eachCourse.getCourseId().equalsIgnoreCase(course.getCourseId())){
						return false;
					} //end if
				} //end for
				
			} //end if
			
			return true;
		} //end if
		
		return false;
	}
	
	/**
	 * This method is to validate the subcourse/semester id that it holds. return true for a valid
	 *  id, else return false.
	 * @param department
	 * @return
	 */
	public boolean validateSubCourseId(String subcourseId){
		if(subcourseId != null && StringUtils.hasLength(subcourseId)){
			SubCourse subcourse = dao.getSubCourse(subcourseId);
			if(subcourse != null && subcourse.getId() > 0){
				return false;
			} //end if
			
			return true;
		} //end if
		
		return false;
	}
	
	// -- Subject related
	
	public List getSubjects(){
		return dao.getSubjects();
	}
	public Subject getSubject(Long id){
		return dao.getSubject(id);
	}
	public List getSubjectBySemester(SubCourse semester){
		return dao.getSubjects(semester);
	}
	public void saveOrUpdateSubject(Subject subject){
		if(subject.getCreatedDate() == null)
			subject.setCreatedDate(new Date());
		dao.saveOrUpdateSubject(subject);
	}
	public void deleteSubject(Subject subject){
		dao.deleteSubject(subject);
	}
	
	public boolean validateSubjectCode(Subject subject){
		if(subject != null && StringUtils.hasLength(subject.getCode())){
			Subject subj = dao.getSubject(subject.getCode());
			if(subj != null && subj.getId() > 0 && !subj.getId().equals(subject.getId())){
				return false;
			} //end if
			
			return true;
		} //end if
		
		return false;
	}
		
	
	// -- User related
	public List getRoles(){
		return Role.getRoles();
	}
	
	public User getUser(Long id){
		return dao.getUser(id);
	}
	
	public List getUsers(){
		return dao.getUsers();
	}
	
	public List getStudents(SubCourse subCourse, User currentUser){
		if(currentUser != null && subCourse != null){
			if(Role.getHODRoles().keySet().contains(currentUser.getRoleId())){
				// if current user has HOD role, then filter every thing by department
				return dao.getUsers(subCourse, new ArrayList(Role.getStudentRoles().keySet()));
			} //
		} //end if
		
		return null;
	}
	
	public List getStaff(User currentUser){
		if(currentUser != null){
			if(Role.getAdminRoles().keySet().contains(currentUser.getRoleId())){
				// if current user has Admin role, then just retrive all users by 
				// roles in HOD and Tutor
				return dao.getUsers(new ArrayList(Role.getStaffRoles().keySet()));
				
			} else if(Role.getHODRoles().keySet().contains(currentUser.getRoleId())){
				// if current user has HOD role, then filter every thing by department
				return dao.getUsers(currentUser.getDepartment(), new ArrayList(Role.getStaffRoles().keySet()));
			} //
		} //end if
		
		return null;
	}
	
	public List getUsers(UserFilter filter){
		if(filter != null){
			List roles = new ArrayList();
			if(UserFilter.USER_ADMIN.equals(filter.getUserType())){
				if(StringUtils.hasLength(filter.getRole())){
					roles.add(filter.getRole());
				} else {
					roles.addAll(Role.getAdminRoles().keySet());
				} //end else
				
				return dao.getUsers(roles);
				
			} else if(UserFilter.USER_STAFF.equals(filter.getUserType())){
				if(StringUtils.hasLength(filter.getRole())){
					roles.add(filter.getRole());
				} else {
					roles.addAll(Role.getStaffRoles().keySet());				
					
				} //end else
				
				for(int tx = 0; tx< roles.size(); tx++)
					System.out.println(roles.get(tx));
				
				if(filter.getDepartment() != null){
					return dao.getUsers(filter.getDepartment(), roles);
				} else {
					return dao.getUsers(roles); 
				} //end else
				
			} else if(UserFilter.USER_STUDENT.equals(filter.getUserType())){
				if(StringUtils.hasLength(filter.getRole())){
					roles.add(filter.getRole());
				} else {
					roles.addAll(Role.getStudentRoles().keySet());
				} //end else
				
				
				
				if(filter.getSubCourse() != null){
					return dao.getUsers(filter.getSubCourse(), roles);
				} if(filter.getCourse() != null){
					return dao.getUsers(filter.getCourse(), roles);
				} else {
					return null;
				} //end else
				
				
			} //end else if
		} //
		
		return null;
	}
	
	public void saveOrUpdateUser(User user){
		if(user.getCreatedDate() == null)
			user.setCreatedDate(new Date());
		
		// if password is updated, the take care of that too.
		if(user.getUpdateLoginDetails()){
			if(StringUtils.hasLength(user.getNewPassword())){
				user.setPassword(user.getNewPassword());
			} //end if
		} //end if
		
		
		dao.saveOrUpdateUser(user);
	}
	public void deleteUser(User user){
		dao.deleteUser(user);
	}
	public boolean checkForUsernameAvailability(User user){
		if(user != null){
			List<User> users = dao.getUsers();
			if(users != null && StringUtils.hasLength(user.getUsername())){
				/*User eachUser = null;
				for(int intx = 0; intx < users.size(); intx++){
					eachUser = (User)users.get(intx);
					if(eachUser != null  
							&& !eachUser.getId().equals(user.getId())
							&& user.getUsername().equalsIgnoreCase(eachUser.getUsername())){
						return false;
					} //end if
				} //end for */
				
				for(User eachUser:users){
					if(eachUser != null  
							&& !eachUser.getId().equals(user.getId())
							&& user.getUsername().equalsIgnoreCase(eachUser.getUsername())){
						return false;
					} //end if
				} //end for
				
			} //end if
			return true;
		} //end if
		return false;
	}
	
	public User validateLogin(String username, String password){
		User user = null;
		if(StringUtils.hasLength(username) && StringUtils.hasLength(password)){
			List<User> users = dao.getUsers();
			if(users != null && users.size() > 0){
				/*User eachUser = null;
				for(int intx = 0; intx < users.size(); intx++){
					eachUser = (User)users.get(intx);
					if(eachUser != null  
							&& username.equals(eachUser.getUsername()) 
							&& password.equals(eachUser.getPassword())){
						return eachUser;
					} //end if
				} //end for */	
				
				for(User eachUser:users){
					if(eachUser != null  
							&& username.equals(eachUser.getUsername()) 
							&& password.equals(eachUser.getPassword())){
						return eachUser;
					} //end if
				} //end for
			} //end if
		} //end if
		return user;
	}

	public boolean validateStaffId(User user){
		if(user != null && StringUtils.hasLength(user.getStaffId())){
			User usr = dao.getUserByStaffId(user.getStaffId());
			if(usr != null && usr.getId() > 0 && !usr.getId().equals(user.getId())){
				return false;
			} //end if
			
			return true;
		} //end if
		
		return false;
	}
	
	public boolean validateStudentId(User user){
		if(user != null && StringUtils.hasLength(user.getStudentId())){
			User usr = dao.getUserByStudentId(user.getStudentId());
			if(usr != null && usr.getId() > 0 && !usr.getId().equals(user.getId())){
				return false;
			} //end if
			
			return true;
		} //end if
		
		return false;
	}
	
	public boolean validateRegistrationNo(User user){
		if(user != null && StringUtils.hasLength(user.getRegistrationNo())){
			User usr = dao.getUserByRegistrationNo(user.getRegistrationNo());
			if(usr != null && usr.getId() > 0 && !usr.getId().equals(user.getId())){
				return false;
			} //end if
			
			return true;
		} //end if
		
		return false;
	}
	
	
	
	// -- Exam related
	public Exam getExam(Long id){
		return dao.getExam(id);
	}
	public void saveOrUpdateExams(Exam exam){
		if(exam.getCreatedDate() == null)
			exam.setCreatedDate(new Date());
		dao.saveOrUpdateExams(exam);
	}
	public void deleteExams(Exam exam){
		dao.deleteExams(exam);
	}
	
	private static final String CLASS_TEST = "CLASS_TEST";
	private static final String MODEL_TEST = "MODEL_TEST";
	private static final String UNIVERSITY_TEST = "UNIVERSITY_TEST";
	private static final String ASSIGNMENT_TEST = "ASSIGNMENT_TEST";
	private static final String SEMINAR_TEST = "SEMINAR_TEST";
	private static final String OTHER_TEST = "OTHER_TEST";
	public Map getExamTypes(){
		Map map = new TreeMap();
		map.put(CLASS_TEST, "Class Test");
		map.put(MODEL_TEST, "Model Test");
		map.put(UNIVERSITY_TEST, "University Test");
		map.put(ASSIGNMENT_TEST, "Assignment");
		map.put(SEMINAR_TEST, "Seminar");
		map.put(OTHER_TEST, "Other Test");
		return map;
	}
	
	public boolean validateExamId(Exam exam){
		if(exam != null && StringUtils.hasLength(exam.getExamId())){
			Exam exm = dao.getExam(exam.getExamId());
			if(exm != null && exm.getId() > 0 && !exm.getId().equals(exam.getId())){
				return false;
			} //end if
			
			return true;
		} //end if
		
		return false;
	}
	
	
	public List getExams(User currentUser, SubCourse subCourse, String examType){
		if(currentUser != null){
			if(Role.getAdminRoles().keySet().contains(currentUser.getRoleId())){
				// if current user has Admin role, then just retrive all users
				if(StringUtils.hasLength(examType))
					return dao.getExams(subCourse, examType);
				else
					return dao.getExams(subCourse);
				
			} else if(Role.getHODRoles().keySet().contains(currentUser.getRoleId())){
				// if current user has HOD role, then filter every thing by department
				if(StringUtils.hasLength(examType))
					return dao.getExams(subCourse, examType);
				else
					return dao.getExams(subCourse);
				
			} else if(Role.getTutorRoles().keySet().contains(currentUser.getRoleId())){
				// if current user has Tutor role, then filter every thing by subjects assigned
				
			} //
		} //end if
		
		return null;
	}
	
	public List getExams(User currentUser, ExamResultFilter filter){
		if(currentUser != null && filter != null){
			if(Role.getAdminRoles().keySet().contains(currentUser.getRoleId())){
				if(StringUtils.hasLength(filter.getExamType()) && filter.getSubCourse() != null){
					return dao.getExams(filter.getSubCourse(), filter.getExamType());
				} else if(filter.getSubCourse() != null){
					return dao.getExams(filter.getSubCourse());
				}
				
			} else if(Role.getHODRoles().keySet().contains(currentUser.getRoleId())){
				if(StringUtils.hasLength(filter.getExamType()) && filter.getSubCourse() != null){
					return dao.getExams(filter.getSubCourse(), filter.getExamType());
				} else if(filter.getSubCourse() != null){
					return dao.getExams(filter.getSubCourse());
				}
				
			} else if(Role.getTutorRoles().keySet().contains(currentUser.getRoleId())){
				if(StringUtils.hasLength(filter.getExamType()) && filter.getSubCourse() != null){
					return dao.getExams(currentUser, filter.getSubCourse(), filter.getExamType());
				} else if(filter.getSubCourse() != null){
					return dao.getExams(currentUser, filter.getSubCourse());
				}
			} //end else if
			
		} //end if
		
		return null;
	}
	
	/**
	 * This will return batches as year starting from 2000 to current Year + 3
	 * ie. if current year is 2007, the list will contain years ..
	 * 2000 t0 2010 .. and as year goes .. automatically the end year will get added.
	 * @return list - List
	 */
	public List getBatches(){
		List<String> list = new ArrayList<String>();
		Calendar cal = Calendar.getInstance();
		cal.setTime(new Date());
		int currentYear = cal.get(Calendar.YEAR);
		for(int intx = 2000; intx < currentYear + 3; intx++){
			list.add("" + intx);
		} //end for
		return list;
	}
	
	
	// -- Subject allotment
	public List getSubjectAllotments(User currentUser, SubjectFilter filter){
		if(currentUser != null && filter != null){
			if(Role.getAdminRoles().keySet().contains(currentUser.getRoleId())){
				// if current user has Admin role, then just retrive all courses by sem
				return dao.getSubjectAllotments(filter.getSubCourse());
				
			} else if(Role.getHODRoles().keySet().contains(currentUser.getRoleId())){
				// if current courses by sem
				return dao.getSubjectAllotments(filter.getSubCourse());
				
			} else if(Role.getTutorRoles().keySet().contains(currentUser.getRoleId())){
				// if current courses by sem
				return dao.getSubjectAllotments(currentUser, filter.getSubCourse());
				
			} //
		} //end if
		
		return null;
	}
	public SubjectAllotment getSubjectAllotment(Long id){
		return dao.getSubjectAllotment(id);
	}
	public void saveOrUpdateSubjectAllotment(SubjectAllotment allotment){
		if(allotment.getCreatedDate() == null){
			// Date added only initial time
			allotment.setCreatedDate(new Date());
		} //end if	
		
		// Date updated on each submit.
		allotment.setLastUpdatedDate(new Date());
		
		dao.saveOrUpdateSubjectAllotment(allotment);
	}
	public void deleteSubjectAllotment(SubjectAllotment allotment){
		dao.deleteSubjectAllotment(allotment);
	}
	public boolean validateSubjectAllotment(SubjectAllotment allotment){
		if(allotment != null){
			List<SubjectAllotment> list = dao.getSubjectAllotments(allotment.getTutor(), allotment.getSubCourse(), allotment.getBatchYear(), allotment.getSubject());
			if(list != null){
				/*SubjectAllotment sa = null;
				for(int intx = 0; intx < list.size(); intx++){
					sa = (SubjectAllotment)list.get(intx);
					if(sa != null && sa.getId() > 0 && !sa.getId().equals(allotment.getId())){
						return false;
					} //end if
				} //end for */
				
				for(SubjectAllotment sa:list){
					if(sa != null && sa.getId() > 0 && !sa.getId().equals(allotment.getId())){
						return false;
					} //end if
				} //end for
			} //end if
			return true;
		} //end if
		
		return false;
	}
	
	/**
	 * Returns list of allotments made to the particular tutor.
	 */
	public List getSubjectAllotmentsBy(User staff){
		return dao.getSubjectAllotments(staff);
	}
	/**
	 * Returns list of allotments made to the particular subject
	 */
	public List getSubjectAllotmentsBy(Subject subject){
		return dao.getSubjectAllotments(subject);
	}
	
	public List getExamResults(Exam exam){
		
		if(exam == null) return null;
		
		// 1. get all students to which this exam is intented to.
		List<User> students1 = dao.getUsers(exam.getSubCourse(), new ArrayList(Role.getStudentRoles().keySet()));
		
		// 2. get students those marks already done.
		List<User> students2 = dao.getUsersInResult(exam);
		
		// 3. remove all students2 from students1;
		students1.removeAll(students2);
		
		// 4. get all exam results so for available in database 
		List<Result> results = dao.getExamResults(exam);
		if(results == null)results = new ArrayList<Result>();
		
		// 5. Add those students left out in students1 list.
		if(students1 != null && students1.size() > 0){
			Result result = null;
			/*User student = null;			
			for(int intx = 0; intx < students1.size(); intx++){
				student = (User)students1.get(intx);
				result = new Result();
				result.setStudent(student);
				result.setExam(exam);
				results.add(result);
			} //end for */	
			for(User student:students1){
				result = new Result();
				result.setStudent(student);
				result.setExam(exam);
				results.add(result);
			} //end for
	} //end if
		
		return results;
	}
	
	public void saveExamResults(List results){
		if(results != null && results.size() > 0){
			dao.saveExamResult(results);
		} //end if
	}
	
	public Map getPerformanceMatrix(PerformanceResultFilter filter){
		Map<String, Object> resultMap = new HashMap<String, Object>();
		resultMap.put("ROW_COUNT", 0);
		if(filter == null) return resultMap;		
		
		/*if("STUDENT_EXAMS".equals(filter.getReportType())){
			// 1. get all exams for the semester
			List<Result> results = dao.getExamResults(filter.getSubject(), filter.getExamType(), filter.getStudent());
			if(results != null && results.size() > 0){
				// adding header exam names
				List<String> headerNames = new ArrayList<String>();
				
				for(Result result:results){
					if(result != null)
					headerNames.add(result.getExam().getName());
				} //end for
				resultMap.put("ROW_0", headerNames);
				
				// adding results
				resultMap.put("ROW_1", results);
				
				// adding the count
				resultMap.put("ROW_COUNT", 2);
			} //end for
			
			
		} else if("STUDENTS_EXAM".equals(filter.getReportType())){
			// 1. get all exams for the semester
			List results = dao.getExamResults(filter.getSubject(), filter.getExamType());
			if(results != null){
				// adding header exam names
				List<String> headerNames = new ArrayList<String>();
				Result result = null;
				int columns = 0;
				Long id = null;
				for(int intx = 0; intx < results.size(); intx++){
					result = (Result)results.get(intx);
					if(intx == 0){
						id = result.getId();
					} //end if
					
					if(!headerNames.contains(result.getExam().getName())){
						headerNames.add(result.getExam().getName());
						columns = intx;
					} else {
						break;
					} //end else 
				} //end for
				resultMap.put("ROW_0", headerNames);
				
				// adding each row entries
				List<Result> rowList = new ArrayList<Result>();	
				int rowListIndext = 1;
				for(int intx = 0; intx < results.size(); intx++){
					result = (Result)results.get(intx);
					if(id.equals(result.getId())){
						rowList.add(result);
						if(intx == (results.size() - 1)){
							resultMap.put("ROW_" + rowListIndext, rowList);
							rowListIndext++;
						} //end if
					} else {
						if(intx < columns){
							for(int i = 0; i< (columns - intx); i++){
								Result r = new Result();
								r.setObtainedMark(0);
								rowList.add(r);
							}
						} //end if
						
						resultMap.put("ROW_" + rowListIndext, rowList);
						rowListIndext++;
						
						rowList = new ArrayList<Result>();
						rowList.add(result);
						id = result.getId();
						
					}
				} //end for
				
				// add the row count
				resultMap.put("ROW_COUNT", rowListIndext);
				
			} //end if
		} */
		
		return resultMap;
	}
}
